/*
 *  $Id: Scene.cs 1231 2010-12-07 12:24:56Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008-2010 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;

using AgateLib;
using AgateLib.DisplayLib;
using AgateLib.Geometry;
using AgateLib.InputLib;

using Nereal.Serialization;

namespace Ragima.UI {
    /// <summary>
    /// Сцена: корневой виджет, охватывающий весь экран.
    /// </summary>
    [TypeId("scene"), XmlRootName("scene"), ReferenceResolver("scene", typeof(string), typeof(Widget))]
    public class Scene : Frame, IKeyResolver<string, Widget> {
        /// <summary>
        /// Время одного фрейма.
        /// </summary>
        public static readonly TimeSpan FrameTime = TimeSpan.FromMilliseconds(18);
        /// <summary>
        /// Идентификатор курсора мыши по умолчанию.
        /// </summary>
        public const string DefaultCursorId = "default";
        /// <summary>
        /// Идентификатор шрифта по умолчанию.
        /// </summary>
        public const string DefaultFontId = "Fonts.Log";

        private Cursor _defaultCursor;
        private Resource<Font> _defaultFont;
        private Widget _hoveredWidget, _focusedWidget, _dragStartWidget;

        /// <summary>
        /// Расположение и размеры сцены по умолчанию равны размеру текушего окна.
        /// </summary>
        public override Rectangle DefaultBounds {
            get { return new Rectangle(Point.Empty, Display.CurrentWindow.Size); }
        }

        /// <summary>
        /// Курсор мыши для всей сцены.
        /// </summary>
        public override Cursor VisibleCursor {
            get { return Cursor ?? DefaultCursor; }
        }

        /// <summary>
        /// Курсор мыши по умолчанию для сцены.
        /// </summary>
        [NotSerialize]
        public Cursor DefaultCursor {
            get { return _defaultCursor ?? (_defaultCursor = new Cursor(DefaultCursorId)); }
        }

        /// <summary>
        /// Шрифт для всей сцены.
        /// </summary>
        public override Font Font {
            get { return Resource.Exists(FontResource) ? FontResource.Data : DefaultFont; }
        }

        /// <summary>
        /// Шрифт по умолчанию для сцены.
        /// </summary>
        [NotSerialize]
        public Font DefaultFont {
            get {
                if (_defaultFont == null)
                    _defaultFont = new Resource<Font>(DefaultFontId);
                return _defaultFont.Data;
            }
        }

        /// <summary>
        /// Виджет, над которым сейчас курсор мыши.
        /// </summary>
        [NotSerialize]
        public Widget HoveredWidget {
            get { return _hoveredWidget; }
        }

        /// <summary>
        /// Виджет, на котором сейчас фокус.
        /// </summary>
        [NotSerialize]
        public Widget FocusedWidget {
            get { return _focusedWidget; }
        }

        /// <summary>
        /// Виджет, на котором начато перетаскивание.
        /// </summary>
        [NotSerialize]
        public Widget DragStartWidget {
            get { return _dragStartWidget; }
        }

        /// <summary>
        /// Инициализирует сцену.
        /// </summary>
        public virtual void InitializeScene() {
            Mouse.MouseMove += PrivMouseMoveHandler;
            Mouse.MouseDown += PrivMouseDownHandler;
            Mouse.MouseUp += PrivMouseUpHandler;
            Mouse.MouseWheel += PrivMouseWheelHandler;
            Keyboard.KeyDown += PrivKeyDownHandler;
        }

        /// <summary>
        /// Финализирует сцену.
        /// </summary>
        public virtual void FinalizeScene() {
            Mouse.MouseMove -= PrivMouseMoveHandler;
            Mouse.MouseDown -= PrivMouseDownHandler;
            Mouse.MouseUp -= PrivMouseUpHandler;
            Mouse.MouseWheel -= PrivMouseWheelHandler;;
            Keyboard.KeyDown -= PrivKeyDownHandler;
        }

        private void PrivMouseMoveHandler(InputEventArgs e) {
            MouseMoveHandler();
        }
        private void PrivMouseDownHandler(InputEventArgs e) {
            ClickHandler(e.MouseButtons, true);
        }
        private void PrivMouseUpHandler(InputEventArgs e) {
            ClickHandler(e.MouseButtons, false);
        }
        private void PrivMouseWheelHandler(InputEventArgs e) {
            MouseWheelHandler(e.WheelDelta);
        }
        private void PrivKeyDownHandler(InputEventArgs e) {
            KeyPressHandler(new KeyboardEvent(e));
        }

        /// <summary>
        /// Ищет виджет под курсором мыши.
        /// </summary>
        private Widget FindHovered() {
            return FindChild(Mouse.Position);
        }

        /// <summary>
        /// Обновляет и обрабатывает виджет под курсором мыши.
        /// </summary>
        public void UpdateHovered() {
            if (DragStartWidget != null)
                return;
            var widget = FindHovered();
            if (widget == _hoveredWidget)
                return;
            if (_hoveredWidget != null)
                _hoveredWidget.DoMouseLeave();
            _hoveredWidget = widget;
            if (_hoveredWidget != null)
                _hoveredWidget.DoMouseEnter();
        }

        /// <summary>
        /// Устанавливает фокус на указанный виджет.
        /// </summary>
        public void SetFocus(Widget widget) {
            if (widget != null && widget.ParentScene != this)
                return;
            if (widget == _focusedWidget)
                return;
            if (_focusedWidget != null)
                _focusedWidget.DoLostFocus();
            _focusedWidget = widget;
            if (_focusedWidget != null)
                _focusedWidget.DoFocus();
        }

        /// <summary>
        /// Начинает процесс перетаскивания (пока он длится, не обновляется виджет под курсором мыши).
        /// </summary>
        /// <param name="widget">Виджет-инициатор перетаскивания.</param>
        public void StartDrag(Widget widget) {
            if (widget == null || widget.ParentScene != this)
                return;
            if (widget == _dragStartWidget)
                return;
            if (_dragStartWidget != null)
                EndDrag();
            _dragStartWidget = widget;
        }

        /// <summary>
        /// Завершает процесс перетаскивания.
        /// </summary>
        public void EndDrag() {
            if (_dragStartWidget == null)
                return;
            _dragStartWidget = null;
            UpdateHovered();
        }

        protected virtual void MouseMoveHandler() {
            UpdateHovered();
            if (HoveredWidget != null)
                HoveredWidget.DoMouseMove(HoveredWidget.MouseLocation);
        }

        protected virtual void ClickHandler(Mouse.MouseButtons buttons, bool down) {
            if (HoveredWidget != null)
                HoveredWidget.DoClick(buttons, down);
        }

        protected virtual void MouseWheelHandler(int delta) {
            var scrollObject = HoveredWidget as IScrollable;
            if (scrollObject != null)
                scrollObject.ScrollBy(delta);
        }

        protected virtual void KeyPressHandler(KeyboardEvent ev) {
            if (FocusedWidget != null)
                if (FocusedWidget.DoKeyPress(ev))
                    return;
            DoKeyPress(ev);
        }

        /// <summary>
        /// Обрабатывает и рисует фрейм по сцене.
        /// </summary>
        public void NextFrame() {
            Process();
            FrameWait();
            if (Display.RenderTarget != null) {
                Display.BeginFrame();
                try {
                    Draw();
                    DrawCursor();
                } finally {
                    Display.EndFrame();
                }
            }
            Core.KeepAlive();
        }

        private DateTime _time = DateTime.Now;
        private void FrameWait() {
            TimeSpan _sleep = FrameTime - (DateTime.Now - _time);
            if (_sleep > TimeSpan.Zero)
                System.Threading.Thread.Sleep(_sleep);
            _time = DateTime.Now;
        }

        /// <summary>
        /// Очищает экран сцены.
        /// </summary>
        protected override void DrawBackground() {
            if (Bounds == DefaultBounds)
                Display.Clear(BackColor ?? Color.Black);
            else
                Display.Clear(BackColor ?? Color.Black, Bounds);
        }

        /// <summary>
        /// Рисует курсор мыши.
        /// </summary>
        public virtual void DrawCursor() {
            var cursor = (_hoveredWidget ?? this).VisibleCursor;
            if (cursor != null)
                cursor.Draw();
        }

        /// <summary>
        /// Обрабатывает сцену.
        /// </summary>
        public override void Process() {
            ProcessChilds();
            UpdateHovered();
        }

        /// <summary>
        /// Обрабатывает событие нажатия клавиши.
        /// </summary>
        public override bool DoKeyPress(KeyboardEvent ev) {
            switch (ev.KeyCode) {
                case KeyCode.Escape:
                    if (FocusedWidget != null)
                        SetFocus(null);
                    return true;
            }
            return base.DoKeyPress(ev);
        }

        #region IKeyResolver[System.String,Widget] implementation
        string IKeyResolver<string, Widget>.ResolveKey(Widget value) {
            return value != null ? value.Id : string.Empty;
        }

        Widget IKeyResolver<string, Widget>.ResolveValue(string key) {
            return FindWidget(key);
        }
        #endregion
    }
}
